import User from '@/models/User.model';
import { verifyPassword } from '@/utils/crypto';
import { generateAccessToken, generateRefreshToken } from '@/utils/jwt';
import logger from '@/utils/logger';
import AuditLog from '@/models/AuditLog.model';
import LoginLog from '@/models/LoginLog.model';
import { AuditAction } from '@/types';
import { getIpLocation, parseUserAgent } from '@/utils/ip';

interface LoginResult {
  user: any;
  accessToken: string;
  refreshToken: string;
}

/**
 * 认证服务
 */
export class AuthService {
  /**
   * 用户登录
   */
  async login(
    email: string,
    password: string,
    tenantId?: string,
    ipAddress?: string,
    userAgent?: string,
  ): Promise<LoginResult> {
    let user: User | null = null;
    const loginTime = new Date();
    const ip = ipAddress || 'unknown';
    const ua = userAgent || '';
    
    // 解析客户端信息
    const { browser, os, device } = parseUserAgent(ua);
    
    try {
      // 查找用户
      const whereClause: any = { email };
      // 超级管理员登录不受租户限制
      if (tenantId && tenantId !== '000000') {
        whereClause.tenantId = tenantId;
      }

      user = await User.findOne({
        where: whereClause,
      });

      if (!user) {
        // 记录失败日志
        await this.recordLoginLog({
          tenantId: tenantId || null,
          userId: null,
          username: email,
          status: 'failed',
          ip,
          userAgent: ua,
          browser,
          os,
          device,
          failReason: '用户不存在',
          loginTime,
        });
        throw new Error('邮箱或密码错误');
      }

      // 验证密码
      const isPasswordValid = await verifyPassword(password, user.passwordHash);
      if (!isPasswordValid) {
        // 记录失败日志
        await this.recordLoginLog({
          tenantId: user.tenantId,
          userId: user.id,
          username: email,
          status: 'failed',
          ip,
          userAgent: ua,
          browser,
          os,
          device,
          failReason: '密码错误',
          loginTime,
        });
        throw new Error('邮箱或密码错误');
      }

      // 检查用户状态
      if (!user.isActive()) {
        // 记录失败日志
        await this.recordLoginLog({
          tenantId: user.tenantId,
          userId: user.id,
          username: email,
          status: 'failed',
          ip,
          userAgent: ua,
          browser,
          os,
          device,
          failReason: '用户已被禁用',
          loginTime,
        });
        throw new Error('用户已被禁用');
      }

      // 检查租户状态
      if (user.tenantId && user.tenantId !== '000000') {
        const Tenant = (await import('@/models/Tenant.model')).default;
        const tenant = await Tenant.findByPk(user.tenantId);
        if (tenant && !tenant.isActive()) {
          // 记录失败日志
          await this.recordLoginLog({
            tenantId: user.tenantId,
            userId: user.id,
            username: email,
            status: 'failed',
            ip,
            userAgent: ua,
            browser,
            os,
            device,
            failReason: '租户已被禁用或暂停',
            loginTime,
          });
          throw new Error('租户已被禁用或暂停');
        }
      }

      // 生成 token
      const payload = {
        userId: user.id,
        tenantId: user.tenantId,
        email: user.email,
        type: user.type as any,
      };

      const accessToken = generateAccessToken(payload);
      const refreshToken = generateRefreshToken(payload);

      // 更新最后登录信息
      await user.update({
        lastLoginAt: loginTime,
        lastLoginIp: ip,
      });

      // 记录成功的登录日志（异步获取IP归属地）
      this.recordLoginLog({
        tenantId: user.tenantId,
        userId: user.id,
        username: email,
        status: 'success',
        ip,
        userAgent: ua,
        browser,
        os,
        device,
        failReason: null,
        loginTime,
      }).catch(err => logger.error('记录登录日志失败:', err));

      // 记录审计日志
      await AuditLog.create({
        tenantId: user.tenantId,
        userId: user.id,
        action: AuditAction.LOGIN,
        resource: 'user',
        resourceId: user.id,
        ipAddress: ip,
        userAgent: ua,
      });

      logger.info(`用户登录成功: ${user.email}`);

      return {
        user: user.toSafeObject(),
        accessToken,
        refreshToken,
      };
    } catch (error) {
      // 如果是未知错误，记录日志
      if (user && error instanceof Error && !error.message.includes('邮箱') && !error.message.includes('密码') && !error.message.includes('禁用')) {
        await this.recordLoginLog({
          tenantId: user.tenantId,
          userId: user.id,
          username: email,
          status: 'failed',
          ip,
          userAgent: ua,
          browser,
          os,
          device,
          failReason: error.message,
          loginTime,
        });
      }
      throw error;
    }
  }

  /**
   * 记录登录日志
   */
  private async recordLoginLog(data: {
    tenantId: string | null;
    userId: string | null;
    username: string;
    status: 'success' | 'failed';
    ip: string;
    userAgent: string;
    browser: string;
    os: string;
    device: string;
    failReason: string | null;
    loginTime: Date;
  }) {
    // 异步获取IP归属地（不阻塞主流程）
    let ipLocation = '未知';
    try {
      ipLocation = await getIpLocation(data.ip);
    } catch (error) {
      logger.error('获取IP归属地失败:', error);
    }

    await LoginLog.create({
      tenantId: data.tenantId,
      userId: data.userId,
      username: data.username,
      loginType: 'password',
      status: data.status,
      ip: data.ip,
      ipLocation,
      userAgent: data.userAgent,
      browser: data.browser,
      os: data.os,
      device: data.device,
      failReason: data.failReason,
      loginTime: data.loginTime,
    });
  }

  /**
   * 刷新 token
   */
  async refreshToken(userId: string): Promise<{ accessToken: string; refreshToken: string }> {
    const user = await User.findByPk(userId);

    if (!user || !user.isActive()) {
      throw new Error('用户不存在或已被禁用');
    }

    const payload = {
      userId: user.id,
      tenantId: user.tenantId,
      email: user.email,
      type: user.type as any,
    };

    const accessToken = generateAccessToken(payload);
    const refreshToken = generateRefreshToken(payload);

    return { accessToken, refreshToken };
  }

  /**
   * 登出
   */
  async logout(userId: string, ipAddress?: string, userAgent?: string): Promise<void> {
    const user = await User.findByPk(userId);

    if (user) {
      // 记录审计日志
      await AuditLog.create({
        tenantId: user.tenantId,
        userId: user.id,
        action: AuditAction.LOGOUT,
        resource: 'user',
        resourceId: user.id,
        ipAddress,
        userAgent,
      });

      logger.info(`用户登出: ${user.email}`);
    }
  }

  /**
   * 修改密码
   */
  async changePassword(
    userId: string,
    oldPassword: string,
    newPassword: string,
  ): Promise<void> {
    const user = await User.findByPk(userId);

    if (!user) {
      throw new Error('用户不存在');
    }

    // 验证旧密码
    const isPasswordValid = await verifyPassword(oldPassword, user.passwordHash);
    if (!isPasswordValid) {
      throw new Error('旧密码错误');
    }

    // 更新密码
    user.password = newPassword;
    await user.save();

    logger.info(`用户修改密码: ${user.email}`);
  }

  /**
   * 重置密码（管理员操作）
   */
  async resetPassword(userId: string, newPassword: string): Promise<void> {
    const user = await User.findByPk(userId);

    if (!user) {
      throw new Error('用户不存在');
    }

    user.password = newPassword;
    await user.save();

    logger.info(`管理员重置用户密码: ${user.email}`);
  }

  /**
   * 更新用户个人信息
   */
  async updateProfile(userId: string, data: {
    firstName?: string;
    lastName?: string;
    avatar?: string;
  }): Promise<User> {
    const user = await User.findByPk(userId);

    if (!user) {
      throw new Error('用户不存在');
    }

    await user.update(data);

    // 记录审计日志
    await AuditLog.create({
      tenantId: user.tenantId,
      userId: user.id,
      action: AuditAction.UPDATE,
      resource: 'user_profile',
      resourceId: user.id,
      changes: data,
    });

    logger.info(`用户更新个人信息: ${user.email}`);

    return user;
  }
}

export default new AuthService();

